package com.game.modular.game.battle.process;

import com.game.comment.exection.ServiceException;
import com.game.modular.game.battle.panel.Battle;
import com.game.modular.game.battle.panel.attribute.domain.PlayerHeroAttribute;
import com.game.modular.game.battle.panel.skill.PanelInfoService;
import com.game.modular.game.battle.info.roundInfo.PvpBattleRoundInfoServiceImpl;
import com.game.modular.game.battle.info.roundInfo.PvpBattleStatisticsInfoServiceImpl;
import com.game.modular.game.battle.panel.attribute.PlayerHeroAttributeServiceImpl;
import com.game.modular.game.battle.process.battleStage.PreparationStageOfRoundService;
import com.game.modular.game.battle.process.battleStage.actionPhase.actionPhase1.PreparationBeforeActionService;
import com.game.modular.game.battle.process.battleStage.actionPhase.actionPhase2.ActionService;
import com.game.modular.game.battle.process.battleStage.actionPhase.actionPhase2.item.ReleaseSkillService;
import com.game.modular.game.battle.process.battleStage.actionPhase.actionPhase2.item.OrdinaryAttackStrategy;
import com.game.modular.game.playerAssets.service.PlayerHeroServiceImpl;
import com.game.modular.game.playerAssets.domain.PlayerHero;
import com.game.modular.game.playerAssets.domain.PvpRanks;
import com.game.modular.game.battle.info.roundInfo.domain.PvpBattleStatisticsInfo;
import com.game.modular.game.playerMatch.service.PlayerMatchServiceImpl;
import com.game.modular.game.playerMatch.domain.PvpCity;
import com.game.modular.game.playerMatch.service.PvpCityServiceImpl;
import com.game.modular.game.playerMatch.service.PvpNpcRanksServiceImpl;
import com.game.modular.game.playerMatch.service.PvpRanksServiceImpl;
import com.game.modular.gameCore.map.service.impl.CoreCityServiceImpl;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import javax.validation.constraints.NotNull;
import java.util.*;
import java.util.stream.Collectors;

import static com.game.modular.game.playerAssets.domain.PlayerHero.obtainBothSidesSurvivalHeroList;
import static com.game.modular.game.battle.info.roundInfo.dto.messageDto.PvpMessageDto.obtainWinnerPlayerId;

@Validated
@Slf4j
@Service
@RequiredArgsConstructor
public class PlayerPvpServiceImpl {
    private final PvpRanksServiceImpl accountRanksService;
    private final PvpCityServiceImpl pvpCityServiceImpl;
    private final PvpRanksServiceImpl accountRanksServiceImpl;
    private final PlayerMatchServiceImpl playerMatchServiceImpl;
    private final ReleaseSkillService releaseSkillService;
    private final PlayerHeroAttributeServiceImpl PlayerHeroAttributeServiceImpl;
    private final PlayerHeroServiceImpl playerHeroServiceImpl;
    private final PvpRanksServiceImpl pvpRanksServiceImpl;
    private final PvpBattleStatisticsInfoServiceImpl pvpBattleStatisticsInfoServiceImpl;
    private final PvpBattleRoundInfoServiceImpl pvpBattleRoundInfoServiceImpl;
    private final PvpNpcRanksServiceImpl pvpNpcRanksServiceImpl;
    private final CoreCityServiceImpl coreCityServiceImpl;
    private final PreparationStageOfRoundService preparationStageOfRound;
    private final PanelInfoService panelInfoService;
    private final PreparationBeforeActionService preparationBeforeActionService;
    private final ActionService actionService;
    private final PlayerHeroAttributeServiceImpl playerHeroAttributeServiceImpl;

    /**
     * 攻打NPC 城池
     */
    @Transactional
    public Map<Long, List<PvpBattleStatisticsInfo>> attackNpcCity(
            @NotNull(message = "玩家id不能为空") Long attackPlayerId,
            @NotNull(message = "队伍编号不能为空") Long attackRankNo,
            @NotNull(message = "攻打的城池不能为空") Long defenseCityId
    ) {
        Long currentMatchId = playerMatchServiceImpl.currentMatchId(attackPlayerId);
        // 获取队伍
        PvpRanks attackRanks = accountRanksService.select(attackPlayerId, String.valueOf(attackRankNo));
        if (attackRanks == null) {
            throw new ServiceException("攻击方队伍未出战");
        }

        // 防守城池
        PvpCity npcPvpCity = pvpCityServiceImpl.getCache(currentMatchId, defenseCityId);
        if (npcPvpCity == null) {
            throw new ServiceException("没有找到npc城池");
        } else if (npcPvpCity.getStatus() == 1) {
            throw new ServiceException("当前城池已被占领");
        } else if (npcPvpCity.getStatus() == 2) {
            throw new ServiceException("当前城池正在被攻击");
        }


        List<PlayerHero> attackSide = accountRanksService.obtainSurvivingGenerals(attackPlayerId, String.valueOf(attackRankNo)).stream()
                .peek(item -> item.setRankCamp(1))
                .collect(Collectors.toList());
        if (attackSide.isEmpty()) {
            throw new ServiceException("攻击方武将没有兵力");
        }

        // npc队伍
        List<PlayerHero> defenseSide = pvpNpcRanksServiceImpl.getNpcCityHero(
                currentMatchId, defenseCityId
        );

        List<PvpBattleStatisticsInfo> pvpBattleStatisticsInfoList = attackTheCityWalls(
                attackPlayerId,
                defenseCityId,
                attackSide,
                defenseSide,
                currentMatchId,
                npcPvpCity
        );

        Long winnerPlayerId = obtainWinnerPlayerId(pvpBattleStatisticsInfoList);
        // 根据结果占领城池

        ranksOccupyTheCity(attackRankNo, npcPvpCity, winnerPlayerId, attackPlayerId, currentMatchId);
        Map<Long, List<PvpBattleStatisticsInfo>> map = new HashMap<>();
        Long npcArmy = coreCityServiceImpl.getCache(npcPvpCity.getCoreCityId()).getNpcArmy();
        map.put(npcArmy, pvpBattleStatisticsInfoList);
        return map;
    }

    @Transactional
    public Map<Long, List<PvpBattleStatisticsInfo>> attackCity(
            @NotNull(message = "玩家id不能为空") Long attackPlayerId,
            @NotNull(message = "队伍编号不能为空") Long attackRanksNo,
            @NotNull(message = "攻打的城池不能为空") Long defenseCityId
    ) {
        Long currentMatchId = playerMatchServiceImpl.currentMatchId(attackPlayerId);
        // 获取队伍
        PvpRanks attackRanks = accountRanksService.select(attackPlayerId, String.valueOf(attackRanksNo));
        if (attackRanks == null) {
            throw new ServiceException("攻击方队伍未出战");
        }

        // 防守城池
        PvpCity city = pvpCityServiceImpl.getPvpCity(defenseCityId, currentMatchId);
        if (city == null) {
            throw new ServiceException("没有找到城池");
        } else if (city.getStatus() == 2) {
            throw new ServiceException("当前城池正在被攻击");
        }

        Long uid = city.getOwnerUid();
        if (Objects.equals(uid, attackPlayerId)) {
            throw new ServiceException("不能攻打自己的城池");
        }
        if (uid == null || uid == 0) {
            return attackNpcCity(attackPlayerId, attackRanksNo, defenseCityId);
        }


        List<PlayerHero> attackSide = accountRanksService.obtainSurvivingGenerals(attackPlayerId, String.valueOf(attackRanksNo)).stream()
                .peek(item -> item.setRankCamp(1))
                .collect(Collectors.toList());
        if (attackSide.isEmpty()) {
            throw new ServiceException("攻击方武将没有兵力");
        }

        // 防守队伍
        Map<Long, List<PlayerHero>> defenseRanksHeroList =
                pvpRanksServiceImpl.getCityRanksHeroList(city.getCoreCityId(), city.getOwnerUid());

        if (defenseRanksHeroList.isEmpty()) {
            ranksOccupyTheCity(attackRanksNo, city, attackPlayerId, attackPlayerId, currentMatchId);
            log.info("没有找到防守队伍直接占领");
        }

        Map<Long, List<PvpBattleStatisticsInfo>> map = new HashMap<>();
        defenseRanksHeroList.forEach((ranksNo, defenseSide) -> {
            List<PvpBattleStatisticsInfo> pvpBattleStatisticsInfos = attackTheCityWalls(
                    attackPlayerId,
                    defenseCityId,
                    attackSide,
                    defenseSide,
                    currentMatchId,
                    city
            );
            map.put(ranksNo, pvpBattleStatisticsInfos);
        });

        List<PvpBattleStatisticsInfo> collect = map.values().stream().flatMap(Collection::stream).collect(Collectors.toList());
        Long winnerPlayerId = obtainWinnerPlayerId(collect);
        // 根据结果占领城池
        if (winnerPlayerId != null && winnerPlayerId.equals(attackPlayerId)) {
            ranksOccupyTheCity(attackRanksNo, city, winnerPlayerId, attackPlayerId, currentMatchId);
        }
        return map;
    }


    /**
     * 队伍对战
     */
    @Transactional
    public List<PvpBattleStatisticsInfo> attackTheCityWalls(
            @NotNull(message = "攻击方玩家id不能为空") Long playerId,
            @NotNull(message = "攻打的城池不能为空") Long defenseCityId,
            @NotNull(message = "攻击方武将不能为空") List<PlayerHero> attackSide,
            @NotNull(message = "防守方武将不能为空") List<PlayerHero> defenseSide,
            @NotNull(message = "当前对战id的对局id不能为空") Long currentMatchId,
            @NotNull(message = "攻打的城池不能为空") PvpCity pvpCity
    ) {
        List<PlayerHero> actionList = new ArrayList<>();
        actionList.addAll(attackSide);
        actionList.addAll(defenseSide);
        actionList.sort((o1, o2) -> o2.getSpeed().compareTo(o1.getSpeed()));

        Set<Long> deathLabel = new HashSet<>();

        List<PvpBattleStatisticsInfo> pvpBattleStatisticsInfoList = new ArrayList<>();
        PvpBattleStatisticsInfo pvpBattleStatisticsInfo = null;
        Long battleId = 0L;
        int roundCount = 0;
        while (true) {
            if (roundCount == 0) {
                log.info("==================准备阶段");
                pvpBattleStatisticsInfo = pvpBattleStatisticsInfoServiceImpl.init(
                        playerId, defenseCityId, attackSide, defenseSide, currentMatchId, pvpCity
                );
                battleId = pvpBattleStatisticsInfo.getBattleId();
                pvpBattleRoundInfoServiceImpl.initPvpBattleRoundInfo(battleId);

                Battle battle = new Battle();
                battle.setBattleId(battleId);
                battle.logBattle();

                PlayerHeroAttributeServiceImpl.init(battleId, actionList);
                panelInfoService.init(battleId, actionList);

                battle.setRound(roundCount);
                battle.save();

                int orderOfAction = 0;
                log.info("==================被动战法");
                for (PlayerHero actWJ : actionList) {
                    orderOfAction++;
                    log.info(",玩家{}武将{}", actWJ.getUid(), actWJ.getName());
                    releaseSkillService.heroReleaseSkills(battleId, roundCount, orderOfAction, actWJ, 2, actionList);
                }

                orderOfAction = 0;
                log.info("==================指挥战法");
                for (PlayerHero actWJ : actionList) {
                    log.info("玩家{}武将{}", actWJ.getUid(), actWJ.getName());
                    orderOfAction++;
                    releaseSkillService.heroReleaseSkills(battleId, roundCount, orderOfAction, actWJ, 3, actionList);
                }
            }

            roundCount++;
            List<PlayerHero> flush = PlayerHeroAttributeServiceImpl.refreshTroops(battleId, actionList);
            Map<Integer, List<PlayerHero>> bothTeams = obtainBothSidesSurvivalHeroList(flush);

            if (bothTeams.size() == 2 && roundCount == 8) {
                endTheRound(null, pvpBattleStatisticsInfo, roundCount, pvpBattleStatisticsInfoList);
                // 结算丢失的兵力
                PlayerHeroAttributeServiceImpl.endRefreshTroops(battleId);

                roundCount = 0;
                // bothTeams.forEach((rankCamp, list) -> {
                //     log.info("camp:{} 存活的武将:", rankCamp);
                //     for (PlayerHero playerHero : list) {
                //         log.info("uid:{} id:{} 名字:{} 存活兵力{}",
                //                 playerHero.getUid(), playerHero.getId(), playerHero.getName(), playerHero.getCurrentForce()
                //         );
                //     }
                // });
                log.info("本场战斗共计死亡的英雄id{}", deathLabel);
                continue;
            }

            if (actionList.isEmpty() || bothTeams.size() == 1 || bothTeams.isEmpty()) {
                Set<Long> deadSet = playerHeroServiceImpl.getDeadSet(battleId, actionList);
                Long winnerId = actionList.stream().filter(item -> !deadSet.contains(item.getId())).collect(Collectors.toList()).get(0).getUid();
                endTheRound(winnerId, pvpBattleStatisticsInfo, roundCount, pvpBattleStatisticsInfoList);
                PlayerHeroAttributeServiceImpl.endRefreshTroops(battleId);
                break;
            }

            int orderOfAction = 0;
            Set<Long> doNotTakeAction = new HashSet<>();// 行动过的武将集合


            Battle battle = Battle.get(battleId);
            battle.setRound(roundCount);
            battle.save();
            List<PlayerHero> heroList = preparationStageOfRound.apply(battleId, roundCount, orderOfAction, flush);

            // 战斗阶段
            for (PlayerHero actWJ : heroList) {
                orderOfAction++;

                List<PlayerHero> targetRange = flush.stream().filter(item -> !deathLabel.contains(item.getId()))// 排除死亡的
                        .collect(Collectors.toList());
                flush = targetRange.stream().filter(item -> !doNotTakeAction.contains(item.getId()))// 排除行动了的
                        .collect(Collectors.toList());

                Map<Integer, List<PlayerHeroAttribute>> rankCampMap =
                        playerHeroAttributeServiceImpl.obtainBothSidesSurvivalHeroList(battleId);

                if (rankCampMap.size() == 1 || targetRange.isEmpty() || flush.isEmpty()) {
                    break;
                }

                if (!doNotTakeAction.contains(actWJ.getId())) {
                    log.info("==================武将行动前阶段,玩家{}武将{}兵力{}", actWJ.getUid(), actWJ.getName(), actWJ.getCurrentForce());
                    preparationBeforeActionService.apply(battleId, actWJ);
                    log.info("==================武将行动阶段,{}{}{}", actWJ.getUid(), actWJ.getName(), actWJ.getCurrentForce());
                    actionService.apply(battleId, roundCount, orderOfAction, actWJ, targetRange);
                    log.info("==================行动结算阶段");
                    recordTags(battleId, deathLabel, doNotTakeAction, actWJ);
                }
            }
        }
        return pvpBattleStatisticsInfoList;
    }

    private void recordTags(Long battleId, Set<Long> deathLabel, Set<Long> doNotTakeAction, PlayerHero actWJ) {
        Set<Long> deadSet = playerHeroAttributeServiceImpl.getDeadSet(battleId);
        doNotTakeAction.add(actWJ.getId());

        List<Long> collect = deadSet.stream().filter(item -> !deathLabel.contains(item)).collect(Collectors.toList());
        deathLabel.addAll(collect);
        log.info("本轮阵亡{}", collect);
        log.info("共计死亡{}", deadSet);
    }


    private void endTheRound(Long winnerId, PvpBattleStatisticsInfo pvpBattleStatisticsInfo, int roundCount, List<PvpBattleStatisticsInfo> pvpBattleStatisticsInfoList) {
        Date date = new Date();
        pvpBattleStatisticsInfo.setWinPlayerId(winnerId);
        pvpBattleStatisticsInfo.setEndTime(date);
        pvpBattleStatisticsInfo.setRoundCount(roundCount);
        pvpBattleStatisticsInfoServiceImpl.updateById(pvpBattleStatisticsInfo);

        pvpBattleStatisticsInfoList.add(pvpBattleStatisticsInfo);
    }


    /**
     * 队伍占领城池
     */
    private void ranksOccupyTheCity(
            @NotNull(message = "攻击方队伍编号不能为空") Long attackRankNo,
            @NotNull(message = "占领的城池不能为空") PvpCity pvpCity,
            @NotNull(message = "占领者id不能为空") Long winnerPlayerId,
            @NotNull(message = "攻击者id不能为空") Long attackPlayerId,
            @NotNull(message = "当前对战id的对局id不能为空") Long currentMatchId
    ) {
        if (winnerPlayerId == null || winnerPlayerId == 0 || !winnerPlayerId.equals(attackPlayerId)) {
            return;
        }
        Long cityCoreId = pvpCity.getCoreCityId();
        pvpCity.setStatus(1);
        // 攻击方获胜
        PvpRanks winnerRanks =
                accountRanksServiceImpl.select(winnerPlayerId, attackRankNo.toString());
        winnerRanks.setCityId(cityCoreId);
        accountRanksServiceImpl.updateBy(winnerPlayerId, winnerRanks.getRankNo(), winnerRanks);

        pvpCityServiceImpl.playerOccupyTheCity(winnerPlayerId, currentMatchId, pvpCity);
    }
}

